package com.tn.openrpc.client
{
	import com.kaazing.gateway.client.protocol.StompClient;
	import com.tn.openrpc.client.event.StompClientEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.getQualifiedClassName;
	import mx.logging.ILogger;
	import mx.logging.Log;
	import mx.utils.ObjectUtil;

	/**
	 * Wrapper for STOMP-CLIENT
	 *
	 * 	Primary built to solve the issue with multiple subscribers to events
	 *
	 * @author neilltyr
	 *
	 */
	public class StompOpenClient extends EventDispatcher implements IStompClient
	{

		/*============================================================================*/
		/* Public Properties                                                          */
		/*============================================================================*/

		private var _connected:Boolean;

		[Bindable(event="connectedChange")]
		public function get connected():Boolean
		{
			return _connected;
		}

		public function set connected(value:Boolean):void
		{
			if (_connected == value)
				return;

			_connected = value;
			dispatchEvent(new Event("connectedChange"));
		}

		/*============================================================================*/
		/* Protected Properties                                                       */
		/*============================================================================*/

		/**
		 * ApacheMQ Credentials
		 * 	eg: { username: "myUser", password: "myPassword" }
		 *
		 * @public
		 */
		protected var credentials:Object;

		/**
		 * ApacheMQ Location (End-point)
		 *
		 * @public
		 */
		protected var location:String;

		/*============================================================================*/
		/* Internal Properties                                                        */
		/*============================================================================*/

		internal var _log:ILogger;

		internal function get log():ILogger
		{
			return _log ||= Log.getLogger(getQualifiedClassName(this).replace("::", "."));
		}

		/*============================================================================*/
		/* Private Properties                                                         */
		/*============================================================================*/

		private var _client:StompClient;

		private function get client():StompClient
		{
			return _client ||= new StompClient();
		}

		/*============================================================================*/
		/* Constructor                                                                */
		/*============================================================================*/

		public function StompOpenClient(location:String, credentials:Object)
		{
			this.location = location;
			this.credentials = credentials;

			init();
		}


		/*============================================================================*/
		/* Public Functions                                                           */
		/*============================================================================*/

		public function ack(messageId:String, transactionId:String = "", receiptId:String = "", headers:* = null):void
		{
			connected && client.ack(messageId, transactionId, receiptId, headers);
		}

		public function begin(id:String, receiptId:String = "", headers:* = null):void
		{
			connected && client.begin(id, receiptId, headers);
		}

		public function commit(id:String, receiptId:String = "", headers:* = null):void
		{
			connected && client.commit(id, receiptId, headers);
		}

		public function connect():void
		{
			if (connected)
				return;

			client.connect(location, credentials);
		}

		public function disconnect():void
		{
			if (connected)
				client.disconnect();
		}

		public function send(body:*, destination:String, transactionId:String = "", receiptId:String = "", headers:* = null):void
		{
			connected && client.send(body, destination, transactionId, receiptId, headers);
		}

		public function subscribe(destination:String, acknowledgement:String = "", receiptId:String = "", headers:* = null):void
		{
			connected && client.subscribe(destination, acknowledgement, receiptId, headers);
		}

		public function unsubscribe(destination:String, receiptId:String = "", headers:* = null):void
		{
			connected && client.unsubscribe(destination, receiptId, headers);
		}

		/*============================================================================*/
		/* Internal Functions                                                         */
		/*============================================================================*/

		internal final function init():void
		{
			client.onreceipt = onReceiptHandler;
			client.onopen = onOpenHandler;
			client.onmessage = onMessageHandler;
			client.onclose = onClosedHandler;
			client.onerror = onErrorHandler;
		}

		internal final function onClosedHandler():void
		{
			(connected = false) && notifySubscribers(StompClientEvent.CLOSE);
		}

		internal final function onErrorHandler(headers:Object, error:String):void
		{
			notifySubscribers(StompClientEvent.ERROR, headers);
		}

		internal final function onMessageHandler(headers:Object, body:*):void
		{
			notifySubscribers(StompClientEvent.MESSAGE, headers, body);
		}

		internal final function onOpenHandler(headers:Object):void
		{
			(connected = true) && notifySubscribers(StompClientEvent.OPEN, headers);
		}

		internal final function onReceiptHandler(headers:Object):void
		{
			notifySubscribers(StompClientEvent.RECEIPT, headers);
		}

		/*============================================================================*/
		/* Private Functions                                                          */
		/*============================================================================*/

		private function notifySubscribers(eventType:String, headers:Object = null, body:* = null):void
		{
			log.info(eventType + ObjectUtil.toString(body) + ObjectUtil.toString(headers));

			if (hasEventListener(eventType))
				dispatchEvent(new StompClientEvent(eventType, headers, body));
		}
	}
}
